import os
import subprocess
import threading
import time
import tkinter as tk
from tkinter import filedialog, scrolledtext

import ttkbootstrap as ttk
from ttkbootstrap.constants import *

from app.util.annotation_util import annotate, ClassInfo
from app.view.base_view import View


@annotate(ClassInfo(name="FtJlinkl", category="ft_tool"))
class FtJlinklView(View):
    """设置视图"""

    def __init__(self, parent, controller):
        super().__init__(parent, controller)

        # 确保中文显示正常
        self.style = ttk.Style()

        # 变量定义
        self.jlink_path = tk.StringVar(value="")
        self.firmware_path = tk.StringVar(value="")
        self.chip_model = tk.StringVar(value="")
        self.interface_type = tk.StringVar(value="SWD")
        self.interface_speed = tk.StringVar(value="4000")
        self.flashing = False

        # 常见芯片型号列表
        self.common_chips = [
            "STM32F103C8T6", "STM32F407IGH6", "STM32L431RCT6",
            "NRF52832", "NRF52840", "AT32F403ACGT7",
            "MK60DN512ZVLL10", "LPC55S69JBD100"
        ]

        # 创建UI组件
        self._create_widgets()

    def _create_widgets(self):
        # 主框架
        main_frame = ttk.Frame(self, padding=20)
        main_frame.pack(fill=BOTH, expand=YES)

        # Jlink路径配置区域
        jlink_frame = ttk.LabelFrame(main_frame, text="Jlink配置", padding=15)
        jlink_frame.pack(fill=X, pady=5)

        ttk.Label(jlink_frame, text="Jlink位置:").grid(row=0, column=0, padx=5, pady=10, sticky=W)

        jlink_entry = ttk.Entry(jlink_frame, textvariable=self.jlink_path, width=50)
        jlink_entry.grid(row=0, column=1, padx=5, pady=10, sticky=EW)

        browse_jlink_btn = ttk.Button(
            jlink_frame,
            text="浏览...",
            command=self.browse_jlink,
            bootstyle=PRIMARY
        )
        browse_jlink_btn.grid(row=0, column=2, padx=5, pady=10)

        jlink_frame.columnconfigure(1, weight=1)

        # 芯片配置区域
        chip_frame = ttk.LabelFrame(main_frame, text="芯片配置", padding=15)
        chip_frame.pack(fill=X, pady=5)

        ttk.Label(chip_frame, text="芯片型号:").grid(row=0, column=0, padx=5, pady=5, sticky=W)

        # 芯片型号下拉框
        chip_combobox = ttk.Combobox(
            chip_frame,
            textvariable=self.chip_model,
            values=self.common_chips,
            width=30
        )
        chip_combobox.grid(row=0, column=1, padx=5, pady=5, sticky=W)

        ttk.Label(chip_frame, text="接口类型:").grid(row=0, column=2, padx=5, pady=5, sticky=W)

        # 接口类型选择
        interface_combobox = ttk.Combobox(
            chip_frame,
            textvariable=self.interface_type,
            values=["JTAG", "SWD"],
            width=10
        )
        interface_combobox.grid(row=0, column=3, padx=5, pady=5, sticky=W)

        ttk.Label(chip_frame, text="接口速度(kHz):").grid(row=0, column=4, padx=5, pady=5, sticky=W)

        # 接口速度选择
        speed_combobox = ttk.Combobox(
            chip_frame,
            textvariable=self.interface_speed,
            values=["1000", "2000", "4000", "8000", "16000"],
            width=10
        )
        speed_combobox.grid(row=0, column=5, padx=5, pady=5, sticky=W)

        chip_frame.columnconfigure(1, weight=1)

        # 固件文件选择区域
        firmware_frame = ttk.LabelFrame(main_frame, text="固件文件", padding=15)
        firmware_frame.pack(fill=X, pady=5)

        ttk.Label(firmware_frame, text="固件位置:").grid(row=0, column=0, padx=5, pady=10, sticky=W)

        firmware_entry = ttk.Entry(firmware_frame, textvariable=self.firmware_path, width=50)
        firmware_entry.grid(row=0, column=1, padx=5, pady=10, sticky=EW)

        browse_firmware_btn = ttk.Button(
            firmware_frame,
            text="浏览...",
            command=self.browse_firmware,
            bootstyle=PRIMARY
        )
        browse_firmware_btn.grid(row=0, column=2, padx=5, pady=10)

        firmware_frame.columnconfigure(1, weight=1)

        # Jlink脚本预览区域
        script_frame = ttk.LabelFrame(main_frame, text="Jlink脚本预览", padding=15)
        script_frame.pack(fill=X, pady=5)

        self.script_text = scrolledtext.ScrolledText(
            script_frame,
            wrap=tk.WORD,
            height=4,
            font=('SimHei', 10)
        )
        self.script_text.pack(fill=X, expand=NO)
        self.script_text.config(state=DISABLED)

        # 操作按钮区域
        btn_frame = ttk.Frame(main_frame, padding=10)
        btn_frame.pack(fill=X, pady=5)

        self.flash_btn = ttk.Button(
            btn_frame,
            text="开始升级",
            command=self.start_flashing,
            bootstyle=SUCCESS,
            width=15
        )
        self.flash_btn.pack(side=LEFT, padx=10)

        self.cancel_btn = ttk.Button(
            btn_frame,
            text="取消",
            command=self.cancel_flashing,
            bootstyle=DANGER,
            width=15,
            state=DISABLED
        )
        self.cancel_btn.pack(side=LEFT, padx=10)

        refresh_script_btn = ttk.Button(
            btn_frame,
            text="刷新脚本",
            command=self.update_script_preview,
            bootstyle=INFO,
            width=15
        )
        refresh_script_btn.pack(side=LEFT, padx=10)

        # 状态显示区域
        status_frame = ttk.LabelFrame(main_frame, text="升级状态", padding=15)
        status_frame.pack(fill=BOTH, expand=YES, pady=5)

        self.status_text = scrolledtext.ScrolledText(
            status_frame,
            wrap=tk.WORD,
            height=10,
            font=('SimHei', 10)
        )
        self.status_text.pack(fill=BOTH, expand=YES)
        self.status_text.config(state=DISABLED)

        # 进度条
        self.progress = ttk.Progressbar(main_frame, mode=DETERMINATE, length=100)
        self.progress.pack(fill=X, pady=10)

        # 初始化状态
        self.update_status("欢迎使用Jlink固件升级工具")
        self.update_status("请配置Jlink路径、选择芯片型号和固件文件，然后点击开始升级")
        self.update_script_preview()

        # 绑定事件，当配置变化时更新脚本预览
        self.chip_model.trace_add("write", lambda *args: self.update_script_preview())
        self.interface_type.trace_add("write", lambda *args: self.update_script_preview())
        self.interface_speed.trace_add("write", lambda *args: self.update_script_preview())
        self.firmware_path.trace_add("write", lambda *args: self.update_script_preview())

    def browse_jlink(self):
        """浏览并选择Jlink可执行文件"""
        file_path = filedialog.askopenfilename(
            title="选择Jlink可执行文件",
            filetypes=[("可执行文件", "*.exe"), ("所有文件", "*.*")]
        )
        if file_path:
            self.jlink_path.set(file_path)
            self.update_status(f"已选择Jlink路径: {file_path}")

    def browse_firmware(self):
        """浏览并选择固件文件"""
        file_path = filedialog.askopenfilename(
            title="选择固件文件",
            filetypes=[("固件文件", "*.hex *.bin *.elf"), ("所有文件", "*.*")]
        )
        if file_path:
            self.firmware_path.set(file_path)
            self.update_status(f"已选择固件文件: {file_path}")

    def update_status(self, message):
        """更新状态显示文本"""
        self.status_text.config(state=NORMAL)
        self.status_text.insert(END, message + "\n")
        self.status_text.see(END)  # 滚动到最新内容
        self.status_text.config(state=DISABLED)

    def update_progress(self, value):
        """更新进度条"""
        self.progress['value'] = value
        self.update_idletasks()

    def update_script_preview(self):
        """更新Jlink脚本预览"""
        chip = self.chip_model.get()
        interface = self.interface_type.get()
        speed = self.interface_speed.get()
        firmware = self.firmware_path.get()

        # 构建Jlink脚本内容
        script_lines = []
        if chip:
            script_lines.append(f"device {chip}")

        if interface == "SWD":
            script_lines.append("si 1")  # SWD模式
        else:
            script_lines.append("si 0")  # JTAG模式

        if speed:
            script_lines.append(f"speed {speed}")

        if firmware:
            script_lines.append(f'loadfile "{firmware}"')
            script_lines.append("r")  # 复位
            script_lines.append("g")  # 运行

        script_lines.append("q")  # 退出

        # 显示脚本内容
        self.script_text.config(state=NORMAL)
        self.script_text.delete(1.0, END)
        self.script_text.insert(END, "\n".join(script_lines))
        self.script_text.config(state=DISABLED)

    def start_flashing(self):
        """开始固件升级过程"""
        jlink_path = self.jlink_path.get()
        firmware_path = self.firmware_path.get()
        chip_model = self.chip_model.get()

        # 验证路径有效性
        if not jlink_path or not os.path.exists(jlink_path):
            self.update_status("错误: Jlink路径无效，请重新选择")
            return

        if not firmware_path or not os.path.exists(firmware_path):
            self.update_status("错误: 固件文件不存在，请重新选择")
            return

        if not chip_model:
            self.update_status("错误: 请选择或输入芯片型号")
            return

        self.flashing = True
        self.update_status("开始升级固件...")
        self.flash_btn.config(state=DISABLED)
        self.cancel_btn.config(state=NORMAL)
        self.update_progress(0)

        # 在新线程中执行升级操作，避免UI冻结
        self.flash_thread = threading.Thread(
            target=self.perform_flashing,
            args=(jlink_path, firmware_path, chip_model)
        )
        self.flash_thread.start()

    def perform_flashing(self, jlink_path, firmware_path, chip_model):
        """执行固件升级的实际操作"""
        try:
            # 更新进度和状态
            self.update_progress(20)
            self.update_status("正在初始化Jlink...")

            # 构建Jlink命令脚本
            script_content = []
            script_content.append(f"device {chip_model}")

            if self.interface_type.get() == "SWD":
                script_content.append("si 1")  # SWD模式
            else:
                script_content.append("si 0")  # JTAG模式

            script_content.append(f"speed {self.interface_speed.get()}")
            script_content.append(f'loadfile "{firmware_path}"')
            script_content.append("r")  # 复位
            script_content.append("g")  # 开始运行
            script_content.append("q")  # 退出

            # 将脚本写入临时文件
            script_path = "jlink_script.tmp"
            with open(script_path, "w") as f:
                f.write("\n".join(script_content))

            self.update_progress(40)
            self.update_status("正在连接目标设备...")

            # 执行Jlink命令
            self.update_progress(60)
            self.update_status("正在烧写固件...")

            # 实际执行Jlink命令
            process = subprocess.Popen(
                [jlink_path, "-CommanderScript", script_path],
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,  # 合并标准输出和错误输出
                text=True,
                bufsize=1,
                universal_newlines=True
            )

            # 实时获取输出并显示
            while self.flashing and process.poll() is None:
                output = process.stdout.readline()
                if output:
                    self.update_status(output.strip())

            # 检查执行结果
            if process.returncode == 0 and self.flashing:
                self.update_progress(80)
                self.update_status("固件烧写完成，正在验证...")
                time.sleep(1)

                # 简单的验证机制 - 检查设备是否可以重新连接
                self.verify_connection(jlink_path, chip_model)
            elif not self.flashing:
                self.update_status("升级已被用户取消")
            else:
                self.update_status(f"升级失败，错误代码: {process.returncode}")

        except Exception as e:
            self.update_status(f"升级过程中发生错误: {str(e)}")
        finally:
            # 清理临时文件
            script_path = "jlink_script.tmp"
            if os.path.exists(script_path):
                try:
                    os.remove(script_path)
                except:
                    pass

            self.flashing = False
            self.flash_btn.config(state=NORMAL)
            self.cancel_btn.config(state=DISABLED)
            if self.progress['value'] < 100:
                self.update_progress(0)

    def verify_connection(self, jlink_path, chip_model):
        """验证设备是否烧录成功并可以连接"""
        try:
            # 创建验证脚本
            verify_script = [
                f"device {chip_model}",
                f"speed {self.interface_speed.get()}",
                "q"
            ]

            verify_script_path = "jlink_verify.tmp"
            with open(verify_script_path, "w") as f:
                f.write("\n".join(verify_script))

            # 执行验证命令
            process = subprocess.Popen(
                [jlink_path, "-CommanderScript", verify_script_path],
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True
            )

            output, _ = process.communicate()

            # 检查输出中是否有成功连接的迹象
            if "Connected" in output or "Found" in output:
                self.update_progress(100)
                self.update_status("固件升级成功！设备验证通过")
            else:
                self.update_status("固件烧写完成，但设备验证失败")
                self.update_status("请检查设备连接或手动验证")

        except Exception as e:
            self.update_status(f"验证过程中发生错误: {str(e)}")
        finally:
            # 清理临时文件
            if os.path.exists(verify_script_path):
                try:
                    os.remove(verify_script_path)
                except:
                    pass

    def cancel_flashing(self):
        """取消固件升级操作"""
        if self.flashing:
            self.update_status("正在取消升级操作...")
            self.flashing = False

    def create_widgets(self):
        pass
